VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsLevelData"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'====================================================================================
'clsLevelData - The core level information
'====================================================================================
'NOTE: Please be careful when editing this page. This same class is used both in
'      the game and the level editor.
Option Explicit

'--- Events -------------------------------------------------------------------------
'When the number of cookies / hearts changes on the map
'Used by the level editor to keep the cookie counter in the corner up to date
Event CookieCountChange(ByVal CookieCount As Long)
Event HeartCountChange(ByVal HeartCount As Long)

'--- Level Structure ----------------------------------------------------------------
'The level layout. See `Load` function for details of this
Private MyTiles() As Byte

Private MyMar As PM_XY              'Mar's starting place on the level
Private MyPen As PM_XY              'The ghost pen's position on the level

Private MyCookieCount As Long       'Number of cookies
Private MyHeartCount As Long        'Number of hearts (power-pills)

'--- Level Attributes ---------------------------------------------------------------
Private MyTheme As String           'Graphical theme of level

'Difficulty level
Public Enum PM_DIFFICULTYLEVEL
    PM_DL_LEARNER = 0
    PM_DL_EASY = 1
    PM_DL_MEDIUM = 2
    PM_DL_HARD = 3
    PM_DL_HARDCORE = 4
    PM_DL_DEBUG = 5
End Enum
Public Difficulty As PM_DIFFICULTYLEVEL

'Level specific high scores
Public HighScores As New clsScoreTableLevel

'--- Graphics -----------------------------------------------------------------------
Private Buffer As New ImageDC
Private WallGfx(15) As ImageDC      'The walls in the theme
Private FloorGfx(5) As New ImageDC  'The floor tiles in the theme

'GET CookieCount
'====================================================================================
Public Property Get CookieCount() As Long
    'There is no LET property for this as the number of cookies is accurately
    'maintained internally, and only changes as and when appropriate, so this value
    'is always accurate
    CookieCount = MyCookieCount
End Property

'GET HeartCount
'====================================================================================
Public Property Get HeartCount() As Long: HeartCount = MyHeartCount: End Property

'GET Tile: Get one tile of the maze, see `Load` for what info is in each tile
'====================================================================================
Public Property Get Tile(ByVal X As Long, ByVal Y As Long) As Byte
    Tile = MyTiles(X, Y)
End Property

'LET Tile: Change something in the maze (Walls / Cookies / Hearts)
'====================================================================================
Public Property Let Tile(ByVal X As Long, ByVal Y As Long, ByVal Value As Byte)
    'Counting how many cookies have changed is tricky:
    '1. If a wall is present, a cookie is still technically there in the data,
    '   just not rendered and should not be counted
    '2. Some cookies are there in the data, but removed from render/count
    '   automatically (for example, under the pen). This is done so that you can
    '   drag the pen around, and retain the cookie layout that was below
    '
    'We therefore have to flag-out these ignored cookies on the previous tile value,
    'and the new tile value to get an accurate count of cookies as rendered on screen
    
    Dim OldValue As Byte, NewValue As Long
    'Account for automatically ignored cookies / walls
    OldValue = ValidateTile(X, Y)
    MyTiles(X, Y) = Value
    NewValue = ValidateTile(X, Y)
    
    'If any of the walls are changed, re-buffer the maze
    If (OldValue And PM_WALL_ALL) <> (NewValue And PM_WALL_ALL) Then
        If Abs(IfBits(NewValue, PM_WALL_TOP)) _
         + Abs(IfBits(NewValue, PM_WALL_RIGHT)) _
         + Abs(IfBits(NewValue, PM_WALL_BOTTOM)) _
         + Abs(IfBits(NewValue, PM_WALL_LEFT)) <> 3 _
        Then
            p_DrawFloor X, Y, Buffer.hDC, (X * TileSize), (Y * TileSize)
        End If
    End If
    
    'Here's a bit of bitwise math that will make your head spin:
    'First of all, if the number of cookies is not the same between the old tile
    'value and the new one,
    If (OldValue And PM_COOKIE_ALL) <> (NewValue And PM_COOKIE_ALL) Then
        'Add or remove the difference.
        '`Abs(IfBits(?Value, PM_COOKIE_?))` returns 1  if that cookie is there,
        'or 0 if it is not. True is -1, so `Abs` just removes the negative.
        'We thus count how many cookies are present on the new value, subtract the
        'number of cookies present on the old value, and add it to the cookie count.
        'If there were more cookies on the old value then the new one, then the
        'cookie count will be decreased appropriately too. Thus this does the work
        'of a whole stack load of `If`s in one instruction
        MyCookieCount = MyCookieCount + _
            (Abs(IfBits(NewValue, PM_COOKIE_TOP)) _
           + Abs(IfBits(NewValue, PM_COOKIE_LEFT)) _
           + Abs(IfBits(NewValue, PM_COOKIE_CENTER))) _
          - (Abs(IfBits(OldValue, PM_COOKIE_TOP)) _
           + Abs(IfBits(OldValue, PM_COOKIE_LEFT)) _
           + Abs(IfBits(OldValue, PM_COOKIE_CENTER)))
        'Alert the class owner about the change
        RaiseEvent CookieCountChange(MyCookieCount)
    
    'And the same for hearts
    ElseIf (OldValue And PM_HEART) <> (NewValue And PM_HEART) Then
        MyHeartCount = MyHeartCount + _
            Abs(IfBits(NewValue, PM_HEART)) _
          - Abs(IfBits(OldValue, PM_HEART))
        'Alert the class owner about the change
        RaiseEvent HeartCountChange(MyHeartCount)
        
    End If
End Property

'GET Width: Return the width of the maze (1-based)
'====================================================================================
Public Property Get Width() As Long: Width = UBound(MyTiles, 1) + 1: End Property

'GET Height: Return the height of the maze (1-based)
'====================================================================================
Public Property Get Height() As Long: Height = UBound(MyTiles, 2) + 1: End Property

'GET MarX: X-Position in the maze that Mar starts on
'====================================================================================
Public Property Get MarX() As Long: MarX = MyMar.X: End Property

'LET MarX: Set X-position in the maze that Mar starts on
'====================================================================================
Public Property Let MarX(ByVal Value As Long)
    MyMar.X = Value
    p_UpdateCounts
End Property

'GET MarY: Y-Position in the maze that Mar starts on
'====================================================================================
Public Property Get MarY() As Long: MarY = MyMar.Y: End Property

'LET MarY: Set Y-position that Mar starts on
'====================================================================================
Public Property Let MarY(ByVal Value As Long)
    MyMar.Y = Value
    p_UpdateCounts
End Property

'GET PenX: X-Position of the ghost pen (left side first) in the maze
'====================================================================================
Public Property Get PenX() As Long: PenX = MyPen.X: End Property

'LET PenX: Set X-position of the ghost pen
'====================================================================================
Public Property Let PenX(ByVal Value As Long)
    If Value <> MyPen.X Then
        MyPen.X = Value
        p_UpdateCounts
        p_CacheMaze
    End If
End Property

'GET PenY: Y-Position of the ghost pen (left side first) in the maze
'====================================================================================
Public Property Get PenY() As Long: PenY = MyPen.Y: End Property

'LET PenY: Set Y-position of the ghost pen
'====================================================================================
Public Property Let PenY(ByVal Value As Long)
    If Value <> MyPen.Y Then
        MyPen.Y = Value
        p_UpdateCounts
        p_CacheMaze
    End If
End Property

'Get Theme
'====================================================================================
Public Property Get Theme() As String: Theme = MyTheme: End Property

'LET Theme
'====================================================================================
Public Property Let Theme(ByVal ThemeName As String)
    Dim K As Long
    
    'If the theme hasn't actually changed, don't bother reloading everything
    If ThemeName = MyTheme Then Exit Property
    'Save the new Theme Name
    MyTheme = ThemeName
    
    Debug.Print "Loaded Theme: " & MyTheme
    For K = 0 To 15
        'The walls are made up of combined numbers where 1=Up, 2=Right, 4=Down and
        '8=Left. 7, 11, 13 and 14 are all combinations where the tunnel is a dead end,
        'which is not supported in this game (ergo, just ignore those slots)
        If (K <> 7) And (K <> 11) And (K <> 13) And (K <> 14) Then
            'Load the Wall for this combination
            Set WallGfx(K) = New ImageDC
            WallGfx(K).CreateFromPicture LoadPicture(AppPath & "\gfx\Themes\" & MyTheme & "\Wall_" & K & ".bmp"), vbCyan
        End If
    Next
    'Load the floor tiles (which have the shadows built in)
    For K = 0 To 5
        FloorGfx(K).CreateFromPicture LoadPicture(AppPath & "\gfx\Themes\" & MyTheme & "\Floor_" & K & ".bmp")
    Next
        
    'Buffer the rendered level
    p_CacheMaze
End Property

'====================================================================================
'Initialize: Define the size of the maze
'====================================================================================
Public Sub Initialize(ByVal NewWidth As Long, ByVal NewHeight As Long)
    ReDim MyTiles(NewWidth - 1, NewHeight - 1) As Byte
    
    Dim X As Long, Y As Long, Cell As Byte
    For Y = 0 To (Me.Height - 1): For X = 0 To (Me.Width - 1)
        'the surrounding walls
        Cell = MyTiles(X, Y)
        If (X = 0) Or (X = Me.Width - 1) Then Cell = PM_WALL_ALL
        If (Y = 0) Or (Y = Me.Height - 1) Then Cell = PM_WALL_ALL
        If (X = 1) Then Cell = AddBits(Cell, PM_WALL_LEFT)
        If (X = Me.Width - 2) Then Cell = AddBits(Cell, PM_WALL_RIGHT)
        If (Y = 1) Then Cell = AddBits(Cell, PM_WALL_TOP)
        If (Y = Me.Height - 2) Then Cell = AddBits(Cell, PM_WALL_BOTTOM)
        'Hearts in each of the four corners
        If Y = 1 Or Y = (Me.Height - 2) Then
            If X = 1 Then Cell = AddBits(Cell, PM_HEART)
            If X = Me.Width - 2 Then Cell = AddBits(Cell, PM_HEART)
        End If
        
        MyTiles(X, Y) = Cell
    Next X: Next Y
    
    'Fill the pen in
    MyPen.X = (Me.Width - 1) \ 2
    MyPen.Y = (Me.Height - 1) \ 2
    
    'Place Mar
    MyMar.X = (Me.Width) \ 2
    MyMar.Y = ((Me.Height - 1) \ 4) * 3
    
    p_UpdateCounts
End Sub

Public Sub RemoveItem(ByVal X As Long, ByVal Y As Long, ByVal Item As PM_TILE)
    Tile(X, Y) = (MyTiles(X, Y) And Not Item) Or (Item And PM_COOKIE_ALL)
End Sub

Public Sub AddItem(ByVal X As Long, ByVal Y As Long, ByVal Item As PM_TILE)
    Tile(X, Y) = MyTiles(X, Y) Or (Item Xor Item And PM_COOKIE_ALL)
End Sub

Public Sub ToggleItem(ByVal X As Long, ByVal Y As Long, ByVal Item As PM_TILE)
    Tile(X, Y) = MyTiles(X, Y) Xor Item
End Sub

Public Function IfItem(ByVal X As Long, ByVal Y As Long, ByVal Item As PM_TILE, Optional ByVal Mask As PM_TILE = -1) As Boolean
    IfItem = IfBits(MyTiles(X, Y), Item, Mask)
End Function

Public Function IfNotItem(ByVal X As Long, ByVal Y As Long, ByVal Item As PM_TILE) As Boolean
    IfNotItem = IfNotBits(MyTiles(X, Y), Item)
End Function

'====================================================================================
'Load: Load a Pac Mar level file (.pmlvl)
'====================================================================================
Public Sub Load(ByVal FilePath As String, Optional ByVal ThemeName As String = "", Optional ByVal DifficultyLevel = -1)
    Dim FormatVersion As Single   'The file format version
    Dim FileTheme As String, FileDifficulty As Long
    Dim NewWidth As Long, NewHeight As Long
    
    'Level data works like this:
    '--------------------------------------------------------------------------------
    'The numbers are added up for the presence of each Wall in a tile.
    '
    '    1
    '  +---+    4 way corridor = 0 (no walls)
    ' 8|   |2   vertical corridor = 10 (walls on 8 + 2, no walls top and bottom)
    '  +---+    etc.
    '    4
    '
    '    o      On screen there appears up to 5 cookies per tile. (If no walls are
    '  o o o    present) But in Pac Mar, the top cookie is the same cookie that is
    '    o      the bottom cookie of the tile above. The same applies to the left
    '           and right cookies! Therefore, only three cookies are needed per tile
    '
    '    16     All cookies are assumed to be present and are drawn unless a cookie
    ' 32 64 --  is removed by adding the numbers on the left to the tile value
    '    --     (above) depending on cookie position. To remove a bottom cookie,
    '           remove the top cookie on the tile below.
    '
    '           Make sure you understand that cookies are *removed* by setting the
    '           relevant cookie bit to *1* not 0. This makes things easier as we
    '           don't have to specify the cookie locations in the level file, only
    '           the ones we want to explicitly remove.
    '
    '    <3     Power Pills (hearts) are added by adding 128 to the tile value.
    '           Power Pills are drawn in the centre of the tile, so make sure you
    '           add 64 to the tile value to switch off the centre cookie too.
    
    Dim FileNumber As Long
    FileNumber = FreeFile
    
    'Check if the level being loaded is the old Beta 2 text format, or the newer
    'binary format (that allows for internal per-level highscores)
    Select Case LCase(Split(FilePath, ".")(UBound(Split(FilePath, "."))))
        Case "pmblvl" '--------------------------------------------------------------
            'Binary format
            Open FilePath For Binary Access Read As #FileNumber
            
            'Get the file format version
            Get #FileNumber, , FormatVersion
            If FormatVersion <> 1# Then Exit Sub
            
            'Get the theme and difficulty level stated in the file
            Dim StringLength As Long
            Get #FileNumber, , StringLength
            FileTheme = Space(StringLength)
            Get #FileNumber, , FileTheme
            Get #FileNumber, , FileDifficulty
            
            'Get maze data
            Get #FileNumber, , NewWidth
            Get #FileNumber, , NewHeight
            ReDim MyTiles(NewWidth - 1, NewHeight - 1) As Byte
            Get #FileNumber, , MyTiles()
            
            'Get Mar / pen location
            Get #FileNumber, , MyMar
            Get #FileNumber, , MyPen
            
            'If there's more data, then the high scores list may be embedded
            If Not (EOF(FileNumber) Or Left(LCase(FilePath), Len(AppPath)) = LCase(AppPath)) Then
                'If the scores data cannot be loaded, it will be ignored
                '(For example, when the scores data format is changed)
                Me.HighScores.Read FileNumber
            End If
            
        Case "pmlvl" '---------------------------------------------------------------
            'Import the old level format
            Open FilePath For Input As #FileNumber
            
            'the first number is a version for the fileformat
            Input #FileNumber, FormatVersion
            
            'If you double click on a level file, the Theme and Difficulty Level
            'inside the file is used to play, instead of the Theme and
            'Difficulty Level dictated by the level sequence when playing the
            'main game
            Input #FileNumber, FileTheme       'Get the default level tileset
            Input #FileNumber, FileDifficulty  'Get the default difficulty level
            
            'Get width and height of the level
            Input #FileNumber, NewWidth
            Input #FileNumber, NewHeight
            ReDim MyTiles(NewWidth - 1, NewHeight - 1) As Byte
            
            'Read in the level layout
            Dim X As Long, Y As Long
            For Y = 0 To (Me.Height - 1): For X = 0 To (Me.Width - 1)
                'Get the binary figure representing the walls and cookies
                Input #FileNumber, MyTiles(X, Y)
            Next X: Next Y
        
            'Place Pac Mar in the starting position
            Input #FileNumber, MyMar.X: Input #FileNumber, MyMar.Y
            
            'Get the location of the pen where the ghosts start
            Input #FileNumber, MyPen.X: Input #FileNumber, MyPen.Y
            
    End Select
    Close #FileNumber
    
    'The original level files in the installed directory cannot be modified,
    'so instead high scores are loaded from auxilary files in %APPDATA%
    If Left(LCase(FilePath), Len(AppPath)) = LCase(AppPath) Then
        Dim SplitString() As String, FileName As String
        SplitString = Split(FilePath, "\")
        FileName = SplitString(UBound(SplitString))
        Me.HighScores.Load Left(FileName, Len(FileName) - Len(Mid(FileName, InStrRev(FileName, "."))))
    End If
    
    'Set the level difficulty
    If DifficultyLevel <> -1 Then FileDifficulty = DifficultyLevel
    Me.Difficulty = FileDifficulty
    
    'Setting the theme will load the graphics, and buffer the rendered level
    If ThemeName <> "" Then FileTheme = ThemeName
    Me.Theme = FileTheme
    p_CacheMaze
    
    'Count the number of cookies on the level
    p_UpdateCounts
End Sub

'====================================================================================
'ValidateTile: Ensures a Tile value is correct (cookies / walls)
'====================================================================================
Public Function ValidateTile(ByVal X As Long, ByVal Y As Long) As Byte
    'Get the raw value
    ValidateTile = MyTiles(X, Y)
    
    'If a Tile is all walls, hide the center cookie in that cell
    '15 = 1(top)+2(right)+4(bottom)+8(left) - All walls!
    If IfBits(ValidateTile, PM_WALL_ALL) Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_CENTER)  'All walls = 0? +Cookie!
    'If a wall is on the top of the Tile, remove the cookie there.
    If IfBits(ValidateTile, PM_WALL_TOP) Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_TOP)       'Top Wall = 0? +Cookie!
    'If a wall is on the left of the Tile, remove the cookie there.
    If IfBits(ValidateTile, PM_WALL_LEFT) Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_LEFT)    'Left Wall = 0? +Cookie!
    
    'Is this Tile in, or around the pen?
    If (X >= MyPen.X And X <= MyPen.X + 2) And _
       (Y >= MyPen.Y - 1 And Y <= MyPen.Y + 1) _
    Then
        If X = (MyPen.X + 2) And Y <> (MyPen.Y + 1) Then
            ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_LEFT)
        
        ElseIf X <> (MyPen.X + 2) And Y = (MyPen.Y + 1) Then
            ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_TOP)
            
        ElseIf X <> (MyPen.X + 2) And Y <> (MyPen.Y + 1) Then
            'Hide all cookies around the pen
            ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_ALL)
        End If
    End If
    
    'Ignore those cookies that overlap the edges of the screen (the left cookies on
    'the leftmost and rightmost level columns)
    If X = 1 Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_LEFT)
    If X = 0 Or X = (Me.Width - 1) Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_ALL)
    
    'Ignore the cookie below Pac Mar
    If (X = MyMar.X) And (Y = MyMar.Y) Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_LEFT)
    
    'Ignore cookies below hearts
    If (ValidateTile And 128) = 128 Then ValidateTile = RemoveBits(ValidateTile, PM_COOKIE_CENTER)
End Function

'====================================================================================
'GetFloorTile: Get which graphic to use for the floor
'====================================================================================
Public Function GetFloorTile(ByVal X As Long, ByVal Y As Long) As Byte
    'Given the wall data for a tile, choose the floor tile to go beneath. The floor
    'tiles have a built in shadow that has to be matched to the walls
    Dim Wall As Byte
    Wall = ValidateTile(X, Y)

    'If this tile is in the ghost pen, use a different floor tile
    If (X = MyPen.X Or X = MyPen.X + 1) And (Y = MyPen.Y) Then
        GetFloorTile = (MyPen.X - X) + 5
        Exit Function
    End If

    If IfBits(Wall, PM_WALL_TOP Or PM_WALL_RIGHT) Then
        ' +---+ Shadow on top and right,
        ' +-+ | Applies only to GetWallData 3 (Walls Top and Right)
        ' +-+-+
        GetFloorTile = 3

    ElseIf IfBits(Wall, PM_WALL_TOP) Then
        ' +---+ Shadow on top
        ' +---+ Applies only to GetWallData 1 (Top Wall), 5 (Top & Bottom wall)
        ' +---+ and 9 (Top and Left Wall)
        GetFloorTile = 1

    ElseIf IfBits(Wall, PM_WALL_RIGHT) Then
        ' +-+-+ Shadow on right
        ' | | | Applies only to GetWallData 2 (Right Wall), 6 (Bottom & Right Wall)
        ' +-+-+ and 10 (Left & Right Wall)
        GetFloorTile = 2

    Else
        ' +-+-+ Shadow in the top right
        ' | +-+ For everything else
        ' +---+
        GetFloorTile = 0

    End If
End Function

Private Sub p_CacheMaze()
    Debug.Print "Cached maze background"
    Dim X As Long, Y As Long
    
    Buffer.Create Me.Width * TileSize, Me.Height * TileSize
    
    For Y = 0 To Me.Height - 1: For X = 0 To Me.Width - 1
        p_DrawFloor X, Y, Buffer.hDC, (X * TileSize), (Y * TileSize)
    Next X: Next Y
End Sub

Private Sub p_DrawFloor(ByVal X As Long, ByVal Y As Long, ByVal hDC As Long, ByVal AtX As Long, ByVal AtY As Long)
    FloorGfx(GetFloorTile(X, Y)).Draw hDC, AtX, AtY
    WallGfx(MyTiles(X, Y) And PM_WALL_ALL).Draw hDC, AtX, AtY
End Sub

Public Sub DrawViewport(ByVal hDC As Long, ByVal DestLeft As Long, ByVal DestTop As Long, ByVal DestWidth As Long, ByVal DestHeight As Long, ByVal SrcLeft As Long, ByVal SrcTop As Long)
    Buffer.DrawEx hDC, DestLeft, DestTop, DestWidth, DestHeight, SrcLeft, SrcTop
End Sub

'====================================================================================
'PRIVATE UpdateCounts: Do a full recount of the number of cookies / hearts
'====================================================================================
Private Sub p_UpdateCounts()
    Dim OldCookieCount As Long, OldHeartCount As Long
    Dim Cell As Byte
    
    'Reset the counts. Remember old values to compare if any actual change occurred
    OldCookieCount = MyCookieCount: MyCookieCount = 0
    OldHeartCount = MyHeartCount: MyHeartCount = 0
    
    Dim X As Long, Y As Long
    For Y = 0 To (Me.Height - 1): For X = 0 To (Me.Width - 1)
        Cell = ValidateTile(X, Y)
        'Remember that a cookie is present when bit 16, 32 or 64 is *0* and gone
        'when the bit is 1, not the other way around. Therefore by using brackets
        'we can return "the result of this comparison", ergo True or False.
        'True = -1 and False = 0, so if we use ABS() to remove the negative sign
        'and turn True into 1 and False into 0.
        MyCookieCount = _
        MyCookieCount + Abs(IfBits(Cell, PM_COOKIE_TOP)) _
                      + Abs(IfBits(Cell, PM_COOKIE_LEFT)) _
                      + Abs(IfBits(Cell, PM_COOKIE_CENTER))
        
        'Check for the presence of a heart
        MyHeartCount = MyHeartCount + Abs(IfBits(Cell, PM_HEART))
    Next X: Next Y
    
    'Raise events for changes
    If OldCookieCount <> MyCookieCount Then RaiseEvent CookieCountChange(MyCookieCount)
    If OldHeartCount <> MyHeartCount Then RaiseEvent HeartCountChange(MyHeartCount)
End Sub

'====================================================================================
'Save: Save a Pac Mar level file
'====================================================================================
Public Function Save(ByVal FilePath As String)
    Dim FileNumber As Long
    FileNumber = FreeFile
    
    If LCase(Right(FilePath, 6)) = ".pmlvl" Then
        'Delete the old file
        Kill FilePath
        'Save in new pmblvl format
        FilePath = Replace(FilePath, ".pmlvl", ".pmblvl")
    End If
    
    Open FilePath For Binary Access Write As #FileNumber
        'Put the file format version
        Put #FileNumber, , CSng(1)
        
        'Put the default theme and difficulty level
        Put #FileNumber, , CLng(Len(MyTheme)): Put #FileNumber, , MyTheme
        Put #FileNumber, , Difficulty
        
        'Put maze data
        Put #FileNumber, , Me.Width
        Put #FileNumber, , Me.Height
        Put #FileNumber, , MyTiles()
        
        'Put Mar / pen location
        Put #FileNumber, , MyMar
        Put #FileNumber, , MyPen
        
        'Save high scores. If a level inside the installed directory, then it will
        'save to an external file, instead of embedding the scores in the file
        HighScores.Save IIf(Left(LCase(FilePath), Len(AppPath)) = LCase(AppPath), 0, FileNumber)
    Close #1
End Function

'====================================================================================
'CLASS Terminate
'====================================================================================
Private Sub Class_Terminate()
    Set Buffer = Nothing
    Erase WallGfx
    Erase FloorGfx
End Sub

'=== END ============================================================================
'This work is licenced under the Creative Commons Attribution 2.5 License
